package com.cjm.one.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjm.one.common.constant.RedisConstants;
import com.cjm.one.common.constant.SecurityConstants;
import com.cjm.one.common.exception.GlobalException;
import com.cjm.one.common.result.ResultCodeEnum;
import com.cjm.one.common.utils.OrderIdGenerator;
import com.cjm.one.coupon.client.CouponInfoClient;
import com.cjm.one.coupon.client.CustomerCouponClient;
import com.cjm.one.goods.client.GoodsSKuClient;
import com.cjm.one.model.base.PageVo;
import com.cjm.one.model.coupon.dto.CartItemDTO;
import com.cjm.one.model.coupon.dto.CouponConfirmDto;
import com.cjm.one.model.coupon.dto.CouponReserveDto;
import com.cjm.one.model.coupon.dto.OrderCalculateDTO;
import com.cjm.one.model.coupon.vo.GoodsDiscountVO;
import com.cjm.one.model.goods.dto.StockDeductionDTO;
import com.cjm.one.model.goods.dto.StockRevertDTO;
import com.cjm.one.model.order.dto.OrderNoRefundDto;
import com.cjm.one.model.order.dto.OrderOperateDTO;
import com.cjm.one.model.order.dto.OrderStatusDto;
import com.cjm.one.model.order.entity.OrderInfo;
import com.cjm.one.model.order.entity.OrderItem;
import com.cjm.one.model.order.enums.OrderParamsTypeEnum;
import com.cjm.one.model.order.enums.OrderStatusEnum;
import com.cjm.one.model.order.enums.PayTypeEnum;
import com.cjm.one.model.order.vo.OrderInfoVo;
import com.cjm.one.order.mapper.OrderItemMapper;
import com.cjm.one.order.service.OrderInfoService;
import com.cjm.one.order.mapper.OrderInfoMapper;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.BatchResult;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 29289
* @description 针对表【order_info(订单)】的数据库操作Service实现
* @createDate 2024-09-05 16:11:25
*/
@Service
@Slf4j
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
    implements OrderInfoService{
    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Resource
    private OrderItemMapper orderItemMapper;

    @Resource
    private GoodsSKuClient goodsSKuClient;
    @Resource
    private CouponInfoClient couponInfoClient;
    @Resource
    private CustomerCouponClient customerCouponClient;
    @Resource
    private RedissonClient redissonClient;


    @Override
    public PageVo<OrderInfoVo> pageList(Long current, Long size,
                                        Long userId, String orderNo,
                                        OrderStatusEnum status, PayTypeEnum payStatus,
                                        String userName, String createTimeStart, String createTimeEnd,
                                        String phone) {
// 参数校验
        if(current < 1) current = 1L;
        if(size < 1 || size > 1000) size = 10L;
        // 计算分页偏移量
        Long offset = (current - 1) * size;
        // 获取分页数据
        List<OrderInfoVo> records = orderInfoMapper.selectPageOrderInfoVo(
                offset, size, userId, orderNo, status, payStatus,
                userName, createTimeStart, createTimeEnd, phone
        );
        // 获取总记录数（需新增mapper方法）
        Long total = orderInfoMapper.selectPageOrderCount(
                userId, orderNo, status, payStatus,
                userName, createTimeStart, createTimeEnd, phone
        );
        // 计算总页数
        Long pages = (total + size - 1) / size;
        // 构建PageVo
        return PageVo.<OrderInfoVo>builder()
                .page(current)
                .limit(size)
                .pages(pages)
                .total(total)
                .records(records)
                .build();
    }

    @Override
    public OrderInfoVo getOrderInfoById(Long id) {
        OrderInfoVo orderInfoVo = orderInfoMapper.selectOrderInfoById(id);
        log.info("orderInfoVo:{}", orderInfoVo);
        return orderInfoVo;
    }

    @Override
    public Boolean deleteOrderById(Long id) {
        LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderItemLambdaQueryWrapper.eq(OrderItem::getOrderId, id);
        int delete = orderItemMapper.delete(orderItemLambdaQueryWrapper);
        if (delete > 0) {
            return orderInfoMapper.deleteById(id) > 0;
        } else {
            return false;
        }

    }

    @Override
    public Boolean refundOrderById(Long id) {
        return null;
    }

    @Override
    public Boolean updateOrderStatusById(Long id, OrderStatusEnum newStatus) {
        LambdaUpdateWrapper<OrderInfo> orderInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        orderInfoLambdaUpdateWrapper.eq(OrderInfo::getId, id);
        orderInfoLambdaUpdateWrapper.set(OrderInfo::getOrderStatus, newStatus);
        OrderInfo orderInfo = orderInfoMapper.selectById(id);
        if (orderInfo == null) {
            return false;
        }
//        订单状态【0->待付款；1->待发货；2->已发货；3->待用户收货，已完成；-1->已取消】
        // 根据当前状态判断是否能修改为新状态
        if (!isStatusTransitionValid(orderInfo.getOrderStatus(), newStatus)) {
            return false;
        }

        int update = orderInfoMapper.update(orderInfo, orderInfoLambdaUpdateWrapper);
        return update>0;
    }

    @Override
    public Boolean deleteBatchOrderByIds(Long[] ids) {

        LambdaQueryWrapper<OrderInfo> orderInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderInfoLambdaQueryWrapper.in(OrderInfo::getId, Arrays.asList(ids));
        int delete1 = orderInfoMapper.delete(orderInfoLambdaQueryWrapper);
        LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderItemLambdaQueryWrapper.in(OrderItem::getOrderId, Arrays.asList(ids));
        int delete = orderItemMapper.delete(orderItemLambdaQueryWrapper);
        return delete1>0;
    }

    @Override
    public Page<OrderInfoVo> getList(Long current, Long size, OrderParamsTypeEnum type, Long id) {
        Page<OrderInfoVo> voPage = new Page<>(current, size);
        List<Integer> statusCodes = getStatusCodes(type);

        Page<OrderInfoVo> page  =   orderInfoMapper.selectPageVo(voPage, statusCodes,id);
        return page;
    }
    private List<Integer> getStatusCodes(OrderParamsTypeEnum type) {
        List<Integer> codes = new ArrayList<>();
        switch (type) {
            case ALL:   // 全部状态（排除已取消）
                codes.addAll(Arrays.stream(OrderStatusEnum.values())
                        .map(OrderStatusEnum::getCode)
                        .collect(Collectors.toList()));
                break;
            case ING:   // 进行中
                codes.addAll(Arrays.asList(OrderStatusEnum.WAIT_PAY.getCode(),
                        OrderStatusEnum.WAIT_DELIVER.getCode(),
                        OrderStatusEnum.ALREADY_DELIVER.getCode()));
                break;
            case WAIT_COMMENT: // 待评价
                codes.add(OrderStatusEnum.WAIT_COMMENT.getCode());
                break;
            case AFTER_SALES: // 售后相关
                codes.addAll(Arrays.asList(
                        OrderStatusEnum.REFUNDING.getCode(),
                        OrderStatusEnum.RETURN_REFUND.getCode(),
                        OrderStatusEnum.REFUNDED.getCode() ));
                break;
            default:
                throw new IllegalArgumentException("无效的订单状态类型");
        }
        return codes;
    }

    @Override
    @Transactional
    public String addOrder(OrderOperateDTO orderOperateDTO) {
        //TODO:后期需要修改优惠卷的使用方法为预占方式，实现流量削峰,更改为使用rabbitMq延迟队列
        //计算订单价格

        List<CartItemDTO> cartItemDTOList = orderOperateDTO.getOrderList().stream()
                .filter(item->item.getSkuNum()>0 && item.getSkuId()>0).map(
                item -> new CartItemDTO(item.getSkuId(), item.getSkuNum())
        ).collect(Collectors.toList());
        OrderCalculateDTO orderCalculateDTO = new OrderCalculateDTO();
        orderCalculateDTO.setCartItems(cartItemDTOList);
        orderCalculateDTO.setUserId(orderOperateDTO.getCustomId());
        //获取优惠信息和真实的商品金额
        List<GoodsDiscountVO> data = couponInfoClient.calculateBestDiscount(orderCalculateDTO, SecurityConstants.INNER).getData();


        if (data.isEmpty()) {
            throw new GlobalException(ResultCodeEnum.ORDER_NO_AMOUNT_ERROR);
        }
        //计算订单金额
        OrderAmountResult orderAmountResult = calculateAccurateAmount(data);
        //创建订单编号
        OrderIdGenerator generator = new OrderIdGenerator(789);
        String OrderNo = generator.generate();
        Integer goodsNum = orderOperateDTO.getOrderList().stream()
                .mapToInt(item -> item.getSkuNum())
                .sum();
        //  创建订单主记录
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderOperateDTO, orderInfo);
        orderInfo.setOrderNo(OrderNo);
        orderInfo.setGoodsNum(goodsNum);
        orderInfo.setOrderStatus(OrderStatusEnum.WAIT_PAY);
        orderInfo.setTotalAmount(orderAmountResult.getTotalAmount());
        orderInfo.setCouponAmount(orderAmountResult.getDiscountAmount());
        orderInfo.setOriginalTotalAmount(orderAmountResult.getTotalAmount().add(orderAmountResult.getDiscountAmount()));
        orderInfo.setFeightFee(BigDecimal.ZERO);
        log.info("orderInfo:{}", orderInfo);
        orderInfoMapper.insert(orderInfo);


        // 2. 批量插入订单明细
        log.info("originItem:{}", orderOperateDTO.getOrderList());
        List<OrderItem> items = orderOperateDTO.getOrderList().stream()
                .map(originItem -> {

                    OrderItem newItem = new OrderItem();
                    //设置可回滚数量
                    BeanUtils.copyProperties(originItem, newItem); // Spring工具类
                    newItem.setRevertQuantity(originItem.getSkuNum());
                    newItem.setOrderId(orderInfo.getId());
                    // 1. 获取商品唯一标识（假设skuId作为键）
                    String skuKey = String.valueOf(newItem.getSkuId());
                    // 2. 从结果集中获取价格明细
                    OrderItemAmount itemAmount = orderAmountResult.getItemAmounts().get(skuKey);
                    // 3. 校验价格数据存在性
                    if (itemAmount == null) {
                        throw new GlobalException(233,"商品价格信息缺失，skuId: " + skuKey);
                    }
                    // 4. 赋值金额字段（含空值保护）
                    newItem.setSaleTotalPrice(
                            Objects.requireNonNullElse(itemAmount.getSaleTotalAmount(), BigDecimal.ZERO)
                    );
                    newItem.setDiscountPrice(
                            Objects.requireNonNullElse(itemAmount.getDiscountTotalAmount(), BigDecimal.ZERO)
                    );
                    newItem.setCouponId(itemAmount.getCouponId());
                    return newItem;
                })
                .collect(Collectors.toList());
        log.info("items:{}", items);
       List<Long> couponIds =  items.stream().filter(item -> item.getCouponId() != null).map(item -> item.getCouponId()).collect(Collectors.toList());
       log.info("couponIds:{}", couponIds);
        if (!couponIds.isEmpty()) {
            //修改优惠卷表的使用数量和领取数量
            CouponConfirmDto couponConfirmDto = new CouponConfirmDto(orderInfo.getId(),couponIds,orderOperateDTO.getCustomId());
            customerCouponClient.batchConfirmUse(couponConfirmDto, SecurityConstants.INNER);

        }
        orderItemMapper.batchInsert(items);

        // 3. 预扣库存
        List<StockDeductionDTO> deductionList = items.stream()
                .map(item -> new StockDeductionDTO(item.getSkuId(), item.getSkuNum(),0L))
                .collect(Collectors.toList());
        try {
            if (!goodsSKuClient.deductionStock(deductionList,SecurityConstants.INNER).getData()) {
                throw new GlobalException(ResultCodeEnum.STOCK_NOT_ENOUGH);
            }
        } catch (Exception e) {
            throw new GlobalException(ResultCodeEnum.STOCK_CHANGE_ERROR);
        }

        // 4. 发送延迟消息（30分钟超时）
        sendDelayMessage(orderInfo.getId());

        return OrderNo;
    }

    /**
     * @description: 发送延迟消息
     * @author: cjm
     * @date: 2025/3/30 16:54
     * @param: [orderId]
     * @return: void
     **/
    private void sendDelayMessage(Long orderId) {
        try {
            RBlockingQueue<String> queue = redissonClient.getBlockingQueue(
                    RedisConstants.ORDER_CANCEL_DELAY_QUEUE);
            RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(queue);

            // 设置30分钟延迟
            delayedQueue.offer(orderId.toString(), 30, TimeUnit.MINUTES);
            log.info("订单延迟消息已发送，orderId: {}", orderId);
        } catch (Exception e) {
            log.error("延迟消息发送失败", e);
            // 回滚预扣库存
            revertStockForOrder(orderId);
            throw new GlobalException(ResultCodeEnum.ORDER_DELAY_MESSAGE_FAIL);
        }
    }

    // 订单回滚时库存回退
    private void revertStockForOrder(Long orderId) {
        List<StockRevertDTO> revertList = orderItemMapper.selectRevertInfoByOrder(orderId);
        if (!revertList.isEmpty()) {
            // 调用库存服务回滚
            Boolean success = goodsSKuClient.revertStock(revertList, SecurityConstants.INNER).getData();

            if (!success) {
                log.error("订单{}库存回滚失败，需人工介入", orderId);
//                TODO 告警服务调用，发送库存回滚失败警告
//                alertService.send("库存回滚失败告警", orderId.toString());
            }else {
                // 5. 更新订单项回滚状态
                orderItemMapper.updateRevertStatus(orderId, revertList);
                log.info("订单{}库存回滚成功", orderId);
            }
        }
    }

    /**
     * @description: 修改订单状态
     * @author: cjm
     * @date: 2025/3/17 21:19
     * @param: [orderInfoVo]
     * @return: java.lang.Boolean
     **/
    @Override
    public Boolean updateOrderById(OrderInfoVo orderInfoVo) {
        return orderInfoMapper.updateOrderById(orderInfoVo) > 0;
    }

    @Override
    public Boolean cancelOrder(String orderId) {
        LambdaUpdateWrapper<OrderInfo> orderInfoLambdaQueryWrapper = new LambdaUpdateWrapper<>();
        orderInfoLambdaQueryWrapper.eq(OrderInfo::getId, orderId);
        orderInfoLambdaQueryWrapper.set(OrderInfo::getOrderStatus, OrderStatusEnum.CANCEL);
        int update = orderInfoMapper.update(null, orderInfoLambdaQueryWrapper);
        return update > 0;
    }

    @Override
    public OrderInfoVo selectByOrderNo( String orderNo) {

        OrderInfoVo orderInfo = orderInfoMapper.selectByNoVo(orderNo);
        return orderInfo;
    }

    @Override
    public Boolean updateByOrderStatus(OrderStatusDto orderStatusDto) {

        LambdaUpdateWrapper<OrderInfo> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(OrderInfo::getId, orderStatusDto.getOrderId());
        wrapper.set(OrderInfo::getOrderStatus, orderStatusDto.getStatus());
// 根据状态设置时间字段
        switch (orderStatusDto.getStatus()) {
            case CANCEL:
                wrapper.set(OrderInfo::getCancelTime, LocalDateTime.now())
                        .set(OrderInfo::getCancelReason, orderStatusDto.getCancelReason());
                break;
            case COMPLETED:
                wrapper.set(OrderInfo::getCompleteTime, LocalDateTime.now());
                break;
            case ALREADY_DELIVER:
                wrapper.set(OrderInfo::getDeliveryTime, LocalDateTime.now());
                break;
            case WAIT_DELIVER:
                wrapper.set(OrderInfo::getPaymentTime, LocalDateTime.now());
                break;
            case RETURN_REFUND:
                wrapper.set(OrderInfo::getStartRefundTime, LocalDateTime.now());
                break;
            case REFUNDING:
                wrapper.set(OrderInfo::getStartRefundTime, LocalDateTime.now());
                break;
            case REFUNDED:
                wrapper.set(OrderInfo::getRefundTime, LocalDateTime.now());
                break;
        }
        int update = orderInfoMapper.update(null, wrapper);
        return update > 0;
    }

    @Override
    public Boolean noRefundOrderByNo(OrderNoRefundDto orderNoRefundDto) {
//        LambdaUpdateWrapper<OrderInfo> wrapper = new LambdaUpdateWrapper<>();
//        wrapper.eq(OrderInfo::getOrderNo, orderNoRefundDto.getOrderNo());
//        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
//        if (orderInfo == null) {
//            throw new GlobalException(ResultCodeEnum.ORDER_NOT_EXIST);
//        }

        LambdaUpdateWrapper<OrderInfo> orderInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        orderInfoLambdaUpdateWrapper.eq(OrderInfo::getOrderNo, orderNoRefundDto.getOrderNo());
        orderInfoLambdaUpdateWrapper.set(OrderInfo::getOrderStatus, OrderStatusEnum.NO_RETURN_REFUND);
        orderInfoLambdaUpdateWrapper.set(OrderInfo::getNoRefundReason, orderNoRefundDto.getNoRefundReason());
        orderInfoLambdaUpdateWrapper.set(OrderInfo::getNoRefundTime, LocalDateTime.now());
        int update = orderInfoMapper.update(null, orderInfoLambdaUpdateWrapper);
        return update > 0;
    }

    @Override
    public Long countPaidUsers(LocalDateTime startTime, LocalDateTime endTime) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(OrderInfo::getPaymentTime, startTime, endTime);
        Long l = orderInfoMapper.selectCount(wrapper);
        if (l == null) {
            return 0L;
        }
        return l;
    }

    private boolean isStatusTransitionValid(OrderStatusEnum currentStatus, OrderStatusEnum newStatus) {
        switch (currentStatus) {
            case WAIT_PAY:
                return newStatus == OrderStatusEnum.WAIT_DELIVER || newStatus == OrderStatusEnum.CANCEL;
            case WAIT_DELIVER:
                return newStatus == OrderStatusEnum.ALREADY_DELIVER || newStatus == OrderStatusEnum.CANCEL || newStatus == OrderStatusEnum.REFUNDING;
            case ALREADY_DELIVER:
                return newStatus == OrderStatusEnum.WAIT_COMMENT  || newStatus == OrderStatusEnum.RETURN_REFUND || newStatus == OrderStatusEnum.REFUNDING;
            case WAIT_COMMENT:
                return newStatus == OrderStatusEnum.COMPLETED || newStatus == OrderStatusEnum.RETURN_REFUND;
            case COMPLETED:
                return  newStatus == OrderStatusEnum.RETURN_REFUND;
            case RETURN_REFUND:
                return  newStatus == OrderStatusEnum.REFUNDED;
            case REFUNDING:
                return  newStatus == OrderStatusEnum.REFUNDED;
            default:
                return false;
        }
    }
    @Data
    @AllArgsConstructor
    public static class OrderAmountResult {
        private final BigDecimal totalAmount; // 总金额
        private final BigDecimal discountAmount; // 总优惠金额
        private final Map<String, OrderItemAmount> itemAmounts; // 每个商品的实际金额
    }
    @Data
    @AllArgsConstructor
    public static class OrderItemAmount{
        private BigDecimal saleTotalAmount;
        private BigDecimal discountTotalAmount;
        private Long couponId;
    }

    public static OrderAmountResult calculateAccurateAmount(List<GoodsDiscountVO> discountList) {
        // 防御性校验
        if (CollectionUtils.isEmpty(discountList)) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode(), "商品优惠数据为空");
        }

        BigDecimal total = BigDecimal.ZERO;
        BigDecimal discountAmount = BigDecimal.ZERO;
        Map<String, OrderItemAmount> itemMap = new LinkedHashMap<>();

        for (GoodsDiscountVO item : discountList) {
            // 参数有效性验证
            validateItemStructure(item);

            // 数值转换（安全处理）
            int quantity = convertQuantity(item.getQuantity());
            BigDecimal originalPrice = convertPrice(item.getOriginalPrice());
            BigDecimal discount = convertDiscount(item.getDiscountAmount());

            // 核心计算逻辑
            BigDecimal itemTotal = originalPrice.multiply(BigDecimal.valueOf(quantity))
                    .subtract(discount)
                    .setScale(2, RoundingMode.HALF_UP);

            // 金额校验
            if (itemTotal.compareTo(BigDecimal.ZERO) < 0) {
                throw new GlobalException(ResultCodeEnum.AMOUNT_CALC_ERROR.getCode(),
                        "SKU:" + item.getSkuId() + " 金额计算异常");
            }
            Long couponId = null;
            if(!Objects.isNull(item.getUsedCoupon())){
                couponId = item.getUsedCoupon().getId();
            }

            // 结果记录
            total = total.add(itemTotal);
            discountAmount = discountAmount.add(discount);
            itemMap.put(item.getSkuId().toString(), new OrderItemAmount(itemTotal,discount,couponId));
        }

        return new OrderAmountResult(total, discountAmount,itemMap);
    }

    // 数值转换方法组
    private static int convertQuantity(Object quantity) {
        try {
            int q = Integer.parseInt(quantity.toString());
            return q > 0 ? q : 1;
        } catch (Exception e) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode(), "数量格式错误");
        }
    }

    private static BigDecimal convertPrice(Object price) {
        try {
            return new BigDecimal(price.toString()).setScale(2, RoundingMode.HALF_UP);
        } catch (Exception e) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode(), "价格格式错误");
        }
    }

    private static BigDecimal convertDiscount(Object discount) {
        if (discount == null) return BigDecimal.ZERO;
        try {
            return new BigDecimal(discount.toString()).setScale(2, RoundingMode.HALF_UP);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }

    // 数据结构校验
    private static void validateItemStructure(GoodsDiscountVO item) {
        if (StringUtils.isBlank(item.getSkuId().toString())) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode(), "SKU ID缺失");
        }
        if (item.getOriginalPrice() == null) {
            throw new GlobalException(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode(), "商品原价未设置");
        }
    }

}




